package com.example.accessibility;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.annotation.SuppressLint;
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Build;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


public class DarknessMagic {
    public abstract static class AccessibilityServiceBase extends AccessibilityService {

        /**
         * 无障碍服务须知：
         * <p>
         * 第一步：
         * <p>
         * 注意:需要在xml 目录中创建 accessible_service_describe.xml
         * 内容如下：
         * <p>
         * <p>
         * <?xml version="1.0" encoding="utf-8"?>
         * <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
         * xmlns:tools="http://schemas.android.com/tools"
         * android:accessibilityEventTypes="typeAllMask"
         * android:accessibilityFeedbackType="feedbackGeneric"
         * android:accessibilityFlags="flagReportViewIds|flagRetrieveInteractiveWindows"
         * android:canPerformGestures="true"
         * android:canRetrieveWindowContent="true"
         * android:description="@string/AccessibleServiceDescribe"
         * android:notificationTimeout="100"
         * tools:ignore="UnusedAttribute" />
         * <p>
         * <!--
         * android:description ：辅助功能描述，描述该辅助功能用来干嘛的
         * android:packageNames ：指定辅助功能监听的包名，不指定表示监听所有应用
         * android:accessibilityEventTypes：辅助功能处理事件类型，一般配置为 typeAllMask 表示接收所有事件
         * android:accessibilityFlags：辅助功能查找截点方式，一般配置为 flagDefault 默认方式。
         * android:accessibilityFeedbackType：操作相应按钮以后辅助功能给用户的反馈类型，包括声音，震动等。
         * android:notificationTimeout：相应时间设置
         * android:canRetrieveWindowContent：是否允许辅助功能获得窗口的节点信息，为了能正常实用辅助功能，请务必保持该项配置为true
         * <p>
         * android:packageNames="com.tencent.mm,com.android.systemui"
         * <p>
         * -->
         * <p>
         * <p>
         * 第二步：在 AndroidManifest.xml 文件中注册该服务
         * <p>
         * <application
         * ...
         * <p>
         * <!-- 无障碍服务 -->
         * <service
         * android:name="创建的无障碍服务类"
         * android:enabled="true"
         * android:exported="true"
         * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
         * <intent-filter>
         * <action android:name="android.accessibilityservice.AccessibilityService" />
         * </intent-filter>
         * <meta-data
         * android:name="android.accessibilityservice"
         * android:resource="@xml/accessible_service_describe" />
         * </service>
         * <p>
         * ...
         * />
         * <p>
         * <p>
         * 第三步：申请无障碍服务并开启
         * <p>
         * //申请无障碍服务
         * if (!DemoService.isStart()) {
         * try {
         * this.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
         * } catch (Exception e) {
         * this.startActivity(new Intent(Settings.ACTION_SETTINGS));
         * e.printStackTrace();
         * }
         * }
         */

        public static DarknessMagic.AccessibilityServiceBase mService;
        public static String RUN_ACTIVITY = "";//运行动作
        public static boolean TF_LOG = false;//是否打开日
        public static boolean RUN_STATE = false;//运行状态// 志

        //初始化
        @Override
        protected void onServiceConnected() {
            super.onServiceConnected();
            if (TF_LOG)
                Log.i("GT","无障碍服务——连接中" );
            mService = this;
            RUN_STATE = true;


        }

        /**
         * 初始化View,会不停的循环此方法
         *
         * @param action
         * @param packageName
         * @param rootInfo
         * @param event
         */
        protected abstract void initView(int action, String packageName, AccessibilityNodeInfo rootInfo, AccessibilityEvent event);

        /**
         * 业务逻辑,当有正确的动作后才会相应此方法
         *
         * @param action
         * @param packageName
         * @param rootInfo
         * @param event
         * @param run_Activity 接受到信息后请立刻清空消息
         */
        protected void loadData(int action, String packageName, AccessibilityNodeInfo rootInfo, AccessibilityEvent event, String run_Activity) {

        }

        //实现辅助功能
        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
            if (TF_LOG)

            Log.i("GT", "无障碍服务——监听中" );
            AccessibilityNodeInfo rootInfo = getRootInActiveWindow();
            if (event == null || rootInfo == null || !RUN_STATE) return;

            if (TF_LOG) {

                Log.i("GT", event.getPackageName().toString());
                Log.i("GT", rootInfo.toString());

            }

            int action = event.getAction();
            CharSequence packageNameCS = event.getPackageName();
            if (packageNameCS == null) return;

            String packageName = String.valueOf(packageNameCS);

            initView(action, packageName, rootInfo, event);

            if (RUN_ACTIVITY == null || RUN_ACTIVITY.length() == 0)
                loadData(event.getAction(), packageName, rootInfo, event, RUN_ACTIVITY);

        }

        @Override
        public void onInterrupt() {
            if (TF_LOG)

            Log.i("GT", "无障碍服务——停止中");
            mService = null;
            RUN_STATE = false;
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            if (TF_LOG) {

                Log.i("GT", "无障碍服务——已关闭");
            }
            mService = null;
            RUN_STATE = false;
        }

        /**
         * 辅助功能是否启动
         */
        public static boolean isStart() {
            return mService != null;
        }

        public static void finish() {
            RUN_STATE = false;
        }

        public static void start() {
            RUN_STATE = true;
        }


        protected void MyGesture() {//仿滑动
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                Path path = new Path();
                path.moveTo(1000, 1000);//滑动起点
                path.lineTo(2000, 1000);//滑动终点
                GestureDescription.Builder builder = new GestureDescription.Builder();
                GestureDescription description = builder.addStroke(new GestureDescription.StrokeDescription(path, 100L, 100L)).build();
                //100L 第一个是开始的时间，第二个是持续时间
                dispatchGesture(description, new DarknessMagic.AccessibilityServiceUtils.MyCallBack(), null);
            }
        }


        /**
         * 立即发送移动的手势
         * 注意7.0以上的手机才有此方法，请确保运行在7.0手机上
         *
         * @param path  移动路径
         * @param mills 持续总时间
         */
        @RequiresApi(24)
        protected void dispatchGestureMove(Path path, long mills) {
            dispatchGesture(new GestureDescription.Builder().addStroke(new GestureDescription.StrokeDescription
                    (path, 0, mills)).build(), null, null);
        }

        /**
         * 点击指定位置
         * 注意7.0以上的手机才有此方法，请确保运行在7.0手机上
         */
        @RequiresApi(24)
        protected void dispatchGestureClick(int x, int y) {
            Path path = new Path();
            path.moveTo(x - 1, y - 1);
            path.lineTo(x + 1, y + 1);
            dispatchGesture(new GestureDescription.Builder().addStroke(new GestureDescription.StrokeDescription
                    (path, 0, 100)).build(), null, null);
        }

        /**
         * 长按指定位置
         * 注意7.0以上的手机才有此方法，请确保运行在7.0手机上
         */
        @RequiresApi(24)
        protected void dispatchGestureLongClick(int x, int y) {
            Path path = new Path();
            path.moveTo(x - 1, y - 1);
            path.lineTo(x, y - 1);
            path.lineTo(x, y);
            path.lineTo(x - 1, y);
            dispatchGesture(new GestureDescription.Builder().addStroke(new GestureDescription.StrokeDescription
                    (path, 0, 1000)).build(), null, null);
        }


            /*  int childCount = rootInfo.getChildCount();
        GT.log("childCount:" + childCount);

        for (int a = 0; a < childCount; a++) {
            AccessibilityNodeInfo child = rootInfo.getChild(a);
            GT.log("child[" + a + "]：" + child);
            if (child == null) continue;

            int childCount1 = child.getChildCount();
            GT.log("childCount1:" + childCount1);

            for (int b = 0; b < childCount1; b++) {
                AccessibilityNodeInfo child1 = child.getChild(b);
                GT.log("child1[" + b + "]：" + child1);
                if (child1 == null) continue;

                if (child1.toString().contains("使用 WLAN")) {
                    GT.log("进入点击 WLAN 流程");
                    RUN_ACTIVITY = "";
                    RUN_STATE = true;
                    clickView(child1);//单击WIFI开关
                    RUN_STATE = false;
                }

                int childCount2 = child1.getChildCount();
                GT.log("childCount2:" + childCount2);

                for (int c = 0; c < childCount2; c++) {
                    AccessibilityNodeInfo child2 = child1.getChild(c);
                    GT.log("child2[" + b + "]：" + child2);
                    if (child2 == null) continue;


                    child2.recycle();
                }


                child1.recycle();//回收资源
                child1 = null;
            }


            child.recycle();//回收资源
            child = null;
        }

        rootInfo.recycle();
        rootInfo = null;


        switch (RUN_ACTIVITY) {
            case "打开WIFI":
                RUN_ACTIVITY = "";//清空动作


                break;

            default:
                MyGesture();//滑动屏幕
                break;
        }*/


    }


    public abstract static class AccessibilityServiceUtils<T> {

        /**
         * 是包含还必须相等;
         */
        protected final boolean mIsEquals;
        protected final T mCheckData;

        public AccessibilityServiceUtils(@NonNull T checkData, boolean isEquals) {
            mCheckData = checkData;
            mIsEquals = isEquals;
        }

        public abstract boolean checkOk(AccessibilityNodeInfo thisInfo);

        /**
         * 找id，就是findAccessibilityNodeInfosByViewId方法
         * 和找text一样效率最高，如果能找到，尽量使用这个
         */
        public static class IdTF extends AccessibilityServiceUtils<String> implements IdTextTF {
            public IdTF(@NonNull String idFullName) {
                super(idFullName, true);
            }

            @Override
            public boolean checkOk(AccessibilityNodeInfo thisInfo) {
                return true;//此处不需要实现
            }

            @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)

            @Override
            public AccessibilityNodeInfo findFirst(AccessibilityNodeInfo root) {
                List<AccessibilityNodeInfo> list = root.findAccessibilityNodeInfosByViewId(mCheckData);
                if (isEmptyArray(list)) {
                    return null;
                }
                for (int i = 1; i < list.size(); i++) {//其他的均回收
                    list.get(i).recycle();
                }
                return list.get(0);
            }

            @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)

            @Override
            public List<AccessibilityNodeInfo> findAll(AccessibilityNodeInfo root) {
                return root.findAccessibilityNodeInfosByViewId(mCheckData);
            }
        }

        /**
         * 普通text，就是findAccessibilityNodeInfosByText方法
         * 和找id一样效率最高，如果能找到，尽量使用这个
         */
        public static class TextTF extends AccessibilityServiceUtils<String> implements IdTextTF {
            public TextTF(@NonNull String text, boolean isEquals) {
                super(text, isEquals);
            }

            @Override
            public boolean checkOk(AccessibilityNodeInfo thisInfo) {
                return true;//此处不需要实现
            }


            @Override
            public AccessibilityNodeInfo findFirst(AccessibilityNodeInfo root) {
                List<AccessibilityNodeInfo> list = root.findAccessibilityNodeInfosByText(mCheckData);
                if (isEmptyArray(list)) {
                    return null;
                }
                if (mIsEquals) {
                    AccessibilityNodeInfo returnInfo = null;
                    for (AccessibilityNodeInfo info : list) {
                        if (returnInfo == null && info.getText() != null && mCheckData.equals(info.getText().toString())) {
                            returnInfo = info;
                        } else {
                            info.recycle();
                        }
                    }
                    return returnInfo;
                } else {
                    return list.get(0);
                }
            }


            @Override
            public List<AccessibilityNodeInfo> findAll(AccessibilityNodeInfo root) {
                List<AccessibilityNodeInfo> list = root.findAccessibilityNodeInfosByText(mCheckData);
                if (isEmptyArray(list)) {
                    return null;
                }
                if (mIsEquals) {
                    ArrayList<AccessibilityNodeInfo> listNew = new ArrayList<>();
                    for (AccessibilityNodeInfo info : list) {
                        if (info.getText() != null && mCheckData.equals(info.getText().toString())) {
                            listNew.add(info);
                        } else {
                            info.recycle();
                        }
                    }
                    return listNew;
                } else {
                    return list;
                }
            }
        }

        /**
         * 类似uc浏览器，有text值但无法直接根据text来找到
         */
        public static class WebTextTF extends AccessibilityServiceUtils<String> {
            public WebTextTF(@NonNull String checkString, boolean isEquals) {
                super(checkString, isEquals);
            }

            @Override
            public boolean checkOk(AccessibilityNodeInfo thisInfo) {
                CharSequence text = thisInfo.getText();
                if (mIsEquals) {
                    return text != null && text.toString().equals(mCheckData);
                } else {
                    return text != null && text.toString().contains(mCheckData);
                }
            }
        }

        /**
         * 找ContentDescription字段
         */
        public static class ContentDescriptionTF extends AccessibilityServiceUtils<String> {
            public ContentDescriptionTF(@NonNull String checkString, boolean isEquals) {
                super(checkString, isEquals);
            }

            @Override
            public boolean checkOk(AccessibilityNodeInfo thisInfo) {
                CharSequence text = thisInfo.getContentDescription();
                if (mIsEquals) {
                    return text != null && text.toString().equals(mCheckData);
                } else {
                    return text != null && text.toString().contains(mCheckData);
                }
            }
        }

        /**
         * 找ClassName匹配
         */
        public static class ClassNameTF extends AccessibilityServiceUtils<String> {
            public ClassNameTF(@NonNull String checkString, boolean isEquals) {
                super(checkString, isEquals);
            }

            @Override
            public boolean checkOk(AccessibilityNodeInfo thisInfo) {
                if (mIsEquals) {
                    return thisInfo.getClassName().toString().equals(mCheckData);
                } else {
                    return thisInfo.getClassName().toString().contains(mCheckData);
                }
            }
        }

        /**
         * 在某个区域内的控件
         */
        public static class RectTF extends AccessibilityServiceUtils<Rect> {
            public RectTF(@NonNull Rect rect) {
                super(rect, true);
            }

            @Override
            public boolean checkOk(AccessibilityNodeInfo thisInfo) {
                thisInfo.getBoundsInScreen(mRecycleRect);
                return mCheckData.contains(mRecycleRect);
            }
        }

        public interface IdTextTF {

            AccessibilityNodeInfo findFirst(AccessibilityNodeInfo root);
            List<AccessibilityNodeInfo> findAll(AccessibilityNodeInfo root);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // 创建方法
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public static Rect mRecycleRect = new Rect();

        public static final String ST_VIEW = "android.view.View",
                ST_TEXTVIEW = "android.widget.TextView",
                ST_IMAGEVIEW = "android.widget.ImageView",
                ST_BUTTON = "android.widget.Button",
                ST_IMAGEBUTTON = "android.widget.ImageButton",
                ST_EDITTEXT = "android.widget.EditText",
                ST_LISTVIEW = "android.widget.ListView",
                ST_LINEARLAYOUT = "android.widget.LinearLayout",
                ST_VIEWGROUP = "android.view.ViewGroup",
                ST_SYSTEMUI = "com.android.systemui";

        /**
         * 找id，就是findAccessibilityNodeInfosByViewId方法
         * 和找text一样效率最高，如果能找到，尽量使用这个
         *
         * @param pageName 被查找项目的包名:com.android.xxx
         * @param idName   id值:tv_main
         */
        public static AccessibilityServiceUtils newId(String pageName, String idName) {
            return newId(pageName + ":id/" + idName);
        }

        /**
         * @param idfullName id全称:com.android.xxx:id/tv_main
         */
        public static AccessibilityServiceUtils newId(@NonNull String idfullName) {
            return new IdTF(idfullName);
        }

        /**
         * 普通text，就是findAccessibilityNodeInfosByText方法
         * 和找id一样效率最高，如果能找到，尽量使用这个
         */
        public static AccessibilityServiceUtils newText(@NonNull String text, boolean isEquals) {
            return new TextTF(text, isEquals);
        }

        /**
         * 类似uc浏览器，有text值但无法直接根据text来找到
         */
        public static AccessibilityServiceUtils newWebText(@NonNull String webText, boolean isEquals) {
            return new WebTextTF(webText, isEquals);
        }

        /**
         * 找ContentDescription字段
         */
        public static AccessibilityServiceUtils newContentDescription(@NonNull String cd, boolean isEquals) {
            return new ContentDescriptionTF(cd, isEquals);
        }

        /**
         * 找ClassName匹配
         */
        public static AccessibilityServiceUtils newClassName(@NonNull String className) {
            return new ClassNameTF(className, true);
        }

        public static AccessibilityServiceUtils newClassName(@NonNull String className, boolean isEquals) {
            return new ClassNameTF(className, isEquals);
        }

        /**
         * 在某个区域内的控件
         */
        public static AccessibilityServiceUtils newRect(@NonNull Rect rect) {
            return new RectTF(rect);
        }


        //模拟手势的监听
        @SuppressLint("NewApi")
        public static class MyCallBack extends AccessibilityService.GestureResultCallback {

            @Override
            public void onCompleted(GestureDescription gestureDescription) {
                super.onCompleted(gestureDescription);

            }

            @Override
            public void onCancelled(GestureDescription gestureDescription) {
                super.onCancelled(gestureDescription);

            }
        }


        /**
         * 点击该控件
         *
         * @return true表示点击成功
         */
        public static boolean clickView(AccessibilityNodeInfo nodeInfo) {
            if (nodeInfo != null) {
                if (nodeInfo.isClickable()) {
                    nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    return true;
                } else {
                    AccessibilityNodeInfo parent = nodeInfo.getParent();
                    if (parent != null) {
                        boolean b = clickView(parent);
                        parent.recycle();
                        if (b) return true;
                    }
                }
            }
            return false;
        }

        /**
         * 查找第一个匹配的控件
         *
         * @param tfs 匹配条件，多个AbstractTF是&&的关系，如：
         *            AbstractTF.newContentDescription("表情", true),AbstractTF.newClassName(AbstractTF.ST_IMAGEVIEW)
         *            表示描述内容是'表情'并且是imageview的控件
         */

        public AccessibilityNodeInfo findFirst(AccessibilityServiceUtils[] tfs, AccessibilityNodeInfo rootInfo) {
            if (tfs.length == 0) throw new InvalidParameterException("AbstractTF不允许传空");

            if (rootInfo == null) return null;

            int idTextTFCount = 0, idTextIndex = 0;
            for (int i = 0; i < tfs.length; i++) {
                if (tfs[i] instanceof AccessibilityServiceUtils.IdTextTF) {
                    idTextTFCount++;
                    idTextIndex = i;
                }
            }
            switch (idTextTFCount) {
                case 0://id或text数量为0，直接循环查找
                    AccessibilityNodeInfo returnInfo = findFirstRecursive(rootInfo, tfs);
                    rootInfo.recycle();
                    return returnInfo;
                case 1://id或text数量为1，先查出对应的id或text，然后再查其他条件
                    if (tfs.length == 1) {
                        AccessibilityNodeInfo returnInfo2 = ((IdTextTF) tfs[idTextIndex]).findFirst(rootInfo);
                        rootInfo.recycle();
                        return returnInfo2;
                    } else {
                        List<AccessibilityNodeInfo> listIdText = ((IdTextTF) tfs[idTextIndex]).findAll(rootInfo);
                        if (isEmptyArray(listIdText)) {
                            break;
                        }
                        AccessibilityNodeInfo returnInfo3 = null;
                        for (AccessibilityNodeInfo info : listIdText) {//遍历找到匹配的
                            if (returnInfo3 == null) {
                                boolean isOk = true;
                                for (AccessibilityServiceUtils tf : tfs) {
                                    if (!tf.checkOk(info)) {
                                        isOk = false;
                                        break;
                                    }
                                }
                                if (isOk) {
                                    returnInfo3 = info;
                                } else {
                                    info.recycle();
                                }
                            } else {
                                info.recycle();
                            }
                        }
                        rootInfo.recycle();
                        return returnInfo3;
                    }
                default:
                    throw new RuntimeException("由于时间有限，并且多了也没什么用，所以IdTF和TextTF只能有一个");
            }
            rootInfo.recycle();
            return null;
        }

        /**
         * @param tfs 由于是递归循环，会忽略IdTF和TextTF
         */
        public static AccessibilityNodeInfo findFirstRecursive(AccessibilityNodeInfo parent, @NonNull AccessibilityServiceUtils... tfs) {
            if (parent == null) return null;
            if (tfs.length == 0) throw new InvalidParameterException("AbstractTF不允许传空");

            for (int i = 0; i < parent.getChildCount(); i++) {
                AccessibilityNodeInfo child = parent.getChild(i);
                if (child == null) continue;
                boolean isOk = true;
                for (AccessibilityServiceUtils tf : tfs) {
                    if (!tf.checkOk(child)) {
                        isOk = false;
                        break;
                    }
                }
                if (isOk) {
                    return child;
                } else {
                    AccessibilityNodeInfo childChild = findFirstRecursive(child, tfs);
                    child.recycle();
                    if (childChild != null) {
                        return childChild;
                    }
                }
            }
            return null;
        }

        /**
         * 查找全部匹配的控件
         *
         * @param tfs 匹配条件，多个AbstractTF是&&的关系，如：
         *            AbstractTF.newContentDescription("表情", true),AbstractTF.newClassName(AbstractTF.ST_IMAGEVIEW)
         *            表示描述内容是'表情'并且是imageview的控件
         */
        @NonNull
        public List<AccessibilityNodeInfo> findAll(AccessibilityServiceUtils[] tfs, AccessibilityNodeInfo rootInfo) {
            if (tfs.length == 0) throw new InvalidParameterException("AbstractTF不允许传空");

            ArrayList<AccessibilityNodeInfo> list = new ArrayList<>();
            if (rootInfo == null) return list;

            int idTextTFCount = 0, idTextIndex = 0;
            for (int i = 0; i < tfs.length; i++) {
                if (tfs[i] instanceof AccessibilityServiceUtils.IdTextTF) {
                    idTextTFCount++;
                    idTextIndex = i;
                }
            }
            switch (idTextTFCount) {
                case 0://id或text数量为0，直接循环查找
                    findAllRecursive(list, rootInfo, tfs);
                    break;
                case 1://id或text数量为1，先查出对应的id或text，然后再循环
                    List<AccessibilityNodeInfo> listIdText = ((IdTextTF) tfs[idTextIndex]).findAll(rootInfo);
                    if (isEmptyArray(listIdText)) {
                        break;
                    }
                    if (tfs.length == 1) {
                        list.addAll(listIdText);
                    } else {
                        for (AccessibilityNodeInfo info : listIdText) {
                            boolean isOk = true;
                            for (AccessibilityServiceUtils tf : tfs) {
                                if (!tf.checkOk(info)) {
                                    isOk = false;
                                    break;
                                }
                            }
                            if (isOk) {
                                list.add(info);
                            } else {
                                info.recycle();
                            }
                        }
                    }
                    break;
                default:
                    throw new RuntimeException("由于时间有限，并且多了也没什么用，所以IdTF和TextTF只能有一个");
            }
            rootInfo.recycle();
            return list;
        }

        /**
         * @param tfs 由于是递归循环，会忽略IdTF和TextTF
         */
        public static void findAllRecursive(List<AccessibilityNodeInfo> list, AccessibilityNodeInfo parent, @NonNull AccessibilityServiceUtils... tfs) {
            if (parent == null || list == null) return;
            if (tfs.length == 0) throw new InvalidParameterException("AbstractTF不允许传空");

            for (int i = 0; i < parent.getChildCount(); i++) {
                AccessibilityNodeInfo child = parent.getChild(i);
                if (child == null) continue;
                boolean isOk = true;
                for (AccessibilityServiceUtils tf : tfs) {
                    if (!tf.checkOk(child)) {
                        isOk = false;
                        break;
                    }
                }
                if (isOk) {
                    list.add(child);
                } else {
                    findAllRecursive(list, child, tfs);
                    child.recycle();
                }
            }
        }


        /**
         * 由于太多,最好回收这些AccessibilityNodeInfo
         */
        public static void recycleAccessibilityNodeInfo(List<AccessibilityNodeInfo> listInfo) {
            if (isEmptyArray(listInfo)) return;

            for (AccessibilityNodeInfo info : listInfo) {
                info.recycle();
            }
        }

        //集合是否是空的
        public static boolean isEmptyArray(Collection list) {
            return list == null || list.size() == 0;
        }

        public static <T> boolean isEmptyArray(T[] list) {
            return list == null || list.length == 0;
        }


    }
}
